﻿using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Primitives;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using BJoin.Common;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
using BJoin.Application.WebApi.Extensions;
using Microsoft.AspNetCore.Mvc;

namespace BJoin.Application.WebApi.Middlewares
{
    public static class AppRunningMiddleware
    {
        public static IApplicationBuilder UseAppStatus(this IApplicationBuilder app, IWebHostEnvironment env, IConfiguration config, string appName, Assembly assemblyHost)
        {
            app.Map("/running-status", builder =>
              builder.Run(async context => {

                  context.Response.ContentType = "application/json";
                  var hostName = Dns.GetHostName();
                  var localIps = Dns.GetHostEntry(hostName).AddressList
                  .Where(al => al.AddressFamily == AddressFamily.InterNetwork)
                  .Select(al => al.ToString());
                  await context.Response.WriteAsync(new
                  {
                      assemblyHost.GetCustomAttribute<AssemblyFileVersionAttribute>()?.Version,
                      assemblyHost.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion,
                      Environment = env.EnvironmentName,
                      AppName = appName,
                      DateTime = DateTime.Now,
                      Environment.MachineName,
                      HostName = hostName,
                      Ips = localIps,
                      Config = new
                      {
                          configSource = config.GetValue<string>("ConfigSource", "None"),
                          configToken = config.GetValue<string>("ConfigToken", "None")
                      }
                  }.TrySerialize());

              }));
            return app;
        }
        public static IApplicationBuilder UseConfigValue(this IApplicationBuilder app, IConfiguration config, string path = "/config-value")
        {
            MapExtensions.Map(app, path, builder =>
              builder.Run(async context =>
              {

                  var key = context.Request.Query["key"];
                  context.Response.ContentType = "application/json";
                  if (StringValues.IsNullOrEmpty(key))
                  {
                      await context.Response.WriteAsync("{ \"message\": \"param key is null.\" }");
                  }
                  else
                  {
                      var cfgKey = key.ToString();
                      await context.Response.WriteAsync(new
                      {
                          Key = cfgKey,
                          Value = config.GetValue<string>(cfgKey),//for section,use ":" get child level value

                      }.TrySerialize());
                  }

              }));

            return app;
        }
        /// <summary>
        /// control loglevel
        /// </summary>
        /// <param name="app"></param>
        /// <param name="path"></param>
        /// <returns></returns>
        public static IApplicationBuilder UseLogLevelController(this IApplicationBuilder app, string path = "/log-levels")
        {
            app.Map(PathString.FromUriComponent(path), subApp => subApp.UseMiddleware<LogLevelControlMiddleware>());
            return app;
        }
    }
    public class SetLogLevelRequest
    {
        public string Name { get; set; }
        public LogLevel MinLevel { get; set; }
    }
    public class LogLevelControlMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILoggerFactory _loggerFactory;

        /// <summary>Creates a new instance of the LogLevelControlMiddleware.</summary>
        /// <param name="next">The next middleware in the pipeline.</param>
        /// <param name="hostingEnv">The <see cref="T:Microsoft.AspNetCore.Hosting.IHostingEnvironment" /> used by this middleware.</param>
        /// <param name="loggerFactory"></param>
        public LogLevelControlMiddleware(RequestDelegate next, IWebHostEnvironment hostingEnv, ILoggerFactory loggerFactory)
        {
            if (hostingEnv == null)
                throw new ArgumentNullException(nameof(hostingEnv));

            _next = next ?? throw new ArgumentNullException(nameof(next));
            _loggerFactory = loggerFactory;
            _next = next;
        }

        /// <summary>
        /// Processes a request
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task Invoke(HttpContext context)
        {
            try
            {
                if (context.Request.Method.Equals(HttpMethods.Get, StringComparison.OrdinalIgnoreCase))
                {
                    var loggerNamePrefix = context.Request.Query["name"];
                    var loggerInfos = _loggerFactory.GetLoggers().Select(l => l.GetInfo());
                    if (!string.IsNullOrWhiteSpace(loggerNamePrefix))
                    {
                        loggerInfos = loggerInfos.Where(i => i.Name.StartsWith(loggerNamePrefix));
                    }

                    context.Response.ContentType = "application/json";
                    await context.Response.WriteAsync(loggerInfos.TrySerialize());
                }
                else if (context.Request.Method.EqualsIgnoreCase(HttpMethods.Post))
                {
                    var requestBody = await context.Request.ReadSteamAsync();
                    var request = requestBody.TryDeserialize<SetLogLevelRequest>();
                    if (request == null)
                    {
                        throw new Exception("request body is null!");
                    }

                    var logger = _loggerFactory.CreateLogger(request.Name);
                    logger.SetMinLevel(request.MinLevel);
                    await context.Response.WriteAsync(new OkResult().TrySerialize());
                }
                else
                {
                    await _next(context);
                }
            }
            catch (Exception e)
            {
                context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                await context.Response.WriteAsync(e.GetBaseException().Message);
            }

        }
    }
}
